package com.ubxtech.gateway.config;

import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.ubxtech.balance.util.ServiceInstanceUtil;
import com.ubxtech.core.hashring.ConsistentHashingVirtualNode;
import com.ubxtech.core.utils.SpringUtils;
import com.ubxtech.gateway.function.CustomPathResourceLookupFunction;
import com.ubxtech.gateway.handler.GlobalExceptionHandler;
import com.ubxtech.gateway.properties.CustomGatewayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Victor.Xiao
 * @since 2023-08-18 18:17
 **/
@Slf4j
@Configuration
public class GatewayConfig {
    /**
     * 自定义异常处理器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalExceptionHandler sentinelGatewayExceptionHandler() {
        return new GlobalExceptionHandler();
    }

    /**
     * Spring 工具类 同core一样要初始化
     *
     * @param applicationContext 上下文
     */
    @Bean
    public SpringUtils getSpringUtils(ApplicationContext applicationContext) {
        SpringUtils instance = SpringUtils.getInstance();
        SpringUtils.setApplicationContext(applicationContext);
        return instance;
    }

    /**
     *
     * 解决 No qualifying bean of type ‘org.springframework.boot.autoconfigure.http.HttpMessageConverters‘
     * 如果要gateway调用微服务,直接调用不行,会抛出一个java.lang.IllegalStateException: block()/blockFirst()/blockLast(),使用CompletableFuture.supplyAsync异步调用解决
     * */
    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
    }


    /**
     * 1. 支持静态资源
     * 2. 支持有默认主页
     * 默认主页:ubxtech.gateway.static-resources.'[/]'=file:C:/xxx/test.html
     * 静态文件访问 ubxtech.gateway.static-resources.'[/test/**]'=file:C:/xxx/
     *
     * 但经过验证,没有nginx速度快,分析有2点待解决的问题
     * 1.每次请求资源,浏览器没使用本地缓存,需添加etag : W/"xxx"或者最后修改时间If-Modified-Since Last-Modified
     * 2. 没有使用js的压缩文件,导致gateway访问的文件比nginx的文件大,比如nginx会压缩或者请求**.js.gz文件,gateway目前都是请求原始文件
     * @author Victor.Xiao
     * @since 2023-10-26 19:58
    */
    @Bean
    public RouterFunction<ServerResponse> staticResourceLocator(ResourceLoader resourceLoader, CustomGatewayProperties customGatewayProperties) {
        //空map不需要启用配置
        if (customGatewayProperties.getStaticResources().isEmpty()) {
            return null;
        }

        RouterFunctions.Builder builder = RouterFunctions.route();
        customGatewayProperties.getStaticResources().forEach((key, value) -> {
            if (log.isDebugEnabled()) {
                log.debug("添加静态资源配置: [{}] -> [{}]", key, value);
            }
//            builder.add(RouterFunctions.resources(key, resourceLoader.getResource(value)));
            //支持端口直接访问html
            builder.add(RouterFunctions.resources(new CustomPathResourceLookupFunction(key, resourceLoader.getResource(value))));
        });
        return builder.build();
    }

    @Bean
    public AppSecretConfigUtil appSecretConfigUtil(CustomGatewayProperties customGatewayProperties) {
        AppSecretConfigUtil appSecretConfigUtil = AppSecretConfigUtil.getInstance();
        AppSecretConfigUtil.setAppSecretMap(customGatewayProperties.getAppSecretConfigs());
        return appSecretConfigUtil;
    }


    /**
     * 初始化一致性hash环
    */
    @Bean
    public ConsistentHashingVirtualNode initConsistentHashingVirtualNode(CustomGatewayProperties customGatewayProperties,DiscoveryClient discoveryClient, NamingService customNamingService, NacosConfigProperties nacosConfigProperties) {

        if (StrUtil.isBlank(customGatewayProperties.getUpDownServiceId())) {
            return null;
        }
        List<String> serviceIds = new ArrayList<>();
        try {
            List<Instance> allInstance = customNamingService.getAllInstances(customGatewayProperties.getUpDownServiceId(),nacosConfigProperties.getGroup());
            for (Instance instance : allInstance) {
                String serviceId = ServiceInstanceUtil.getServiceId(instance);
                if (StrUtil.isNotBlank(serviceId)) {
                    serviceIds.add(serviceId);
                }

            }
        } catch (NacosException e) {
            log.error("初始化 ConsistentHashingVirtualNode失败",e);
        }
        if (log.isDebugEnabled()) {
            log.debug("初始化 ConsistentHashingVirtualNode: {}", serviceIds);
        }
        return new ConsistentHashingVirtualNode(serviceIds);
    }
}
